package com.jt.demo2.aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.util.Arrays;

//AOP面向切面编程,动态代理代码被AOP的API封装
//知识铺垫 : 切面 = 动态代理 + 方法的扩展

@Component //将这个类交给spring容器管理
@Aspect //标识当前类是一个切面
@Order(value = 1) //数字越小,越先执行
public class TxAspect {

    //编码 : 切面 = 切入点表达式 + 通知方法
    //切入点表达式 : 为谁创建代理对象!!!
    //@Pointcut("bean(userServiceImpl)") //标识切入点表达式,一个
    //@Pointcut("within(com.jt.demo2.service.*)") //标识切入点表达式,多个
    //@Pointcut("execution(* com.jt.demo2.service..*.*(..))") //常用,因为是细粒度且能改变
    //如何创建自定义注解,元注解!!!
    @Pointcut("@annotation(com.jt.demo2.anno.TX)") //常用,自定义注解
    public void pointCut() {

    }
    //通知方法 : 对原有方法进行拓展
    //前置通知
    //@Before("pointCut()") //通知注解
    public void before(JoinPoint joinPoint) {
        //1.获取目标对象的类型
        Class<?> targetClass = joinPoint.getTarget().getClass();
        System.out.println("目标对象的类型:" + targetClass);

        //2.获取目标对象的名称
        String targetName = joinPoint.getSignature().getDeclaringTypeName();
        System.out.println("目标对象的名称:" + targetName);

        //3.获取目标方法的名称
        String methodName = joinPoint.getSignature().getName();
        System.out.println("目标方法的名称:" + methodName);

        //4.获取参数的数据
        Object[] args = joinPoint.getArgs();
        System.out.println("参数的名称:" + Arrays.toString(args));

        System.out.println("AOP前置通知");
    }

    /**
     * 记录目标方法的返回值的结果
     * returning : 后置通知获取返回值的属性
     */
    //后置通知,可以记录目标方法的返回值的结果
    //@AfterReturning(value = "pointCut()", returning = "result")
    public void afterReturning(Object result) {
        System.out.println("目标方法的返回值:" + result);
        System.out.println("AOP后置通知");
    }

    /**
     * 说明 : 如果程序执行抛出异常,则可以由异常通知进行记录
     * throwing : 抛出异常的属性
     */
    //异常通知
    //@AfterThrowing(value = "pointCut()", throwing = "exception")
    public void afterThrowing(JoinPoint joinPoint, Exception exception) {
        System.out.println("目标方法抛出的异常信息:" + exception.getMessage());
        System.out.println("运行时抛出异常!!!");
    }

    //最终通知
    //@After("pointCut()")
    public void after() {
        System.out.println("我是最终通知");
    }

    //环绕通知,使用最多
    /**
     * 注意事项 : 环绕通知中必须添加参数,并且必须位于第一位
     * 用法 :
     *        Proceed with the next advice or target method invocation
     *        1.如果有下一个通知,则运行下一个通知
     *        2.如果没有下一个通知则执行目标方法
     * @return
     */
    @Around("pointCut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("环绕通知开始");
        Object result = joinPoint.proceed();
        System.out.println("环绕通知结束");
        return result;
    }
}
